<?php

/**
 * A PDO connection class.
 * Offers connectivity with SQLite databases and others through a standard interface.
 * @package Nina
 * @author John Main <john@thedaddy.org>, Chris Corbyn, Andrew Rose
 */
class Nina_Db_Driver_Pdo implements Nina_Db_Driver
{
	/**
	 * The PDO instance.
	 * @var PDO
	 */
	protected $pdo = null;

	/**
	 * The registry instance.
	 * @var Nina_Registry
	 */
	protected $reg = null;
	
	/**
	 * Connect to the database.
	 * @param Nina_Registry $reg Registry object
	 * @throws Nina_Exception_Db If the connection fails.
	 */
	public function init(Nina_Registry $reg) {
		$this->reg = $reg;

		if (!$reg->exists('dsn')) {
			throw new Nina_Exception_Db_Driver("PDO requires a valid DSN to connect with.  Missing 'dsn' in registry.");
		}
		if(!class_exists('PDO')) {
			throw new Nina_Exception_Db_Driver("PDO extension does not appear to be loaded.");
		}

		if($this->connect()) {
			if($this->reg->exists('database') && !$this->database())
				throw new Nina_Exception_Db('Unable to select database ' . $this->reg->get('database'));
			else
				return true;
		}
		else
			throw new Nina_Exception_Db('Unable to establise database connection');

		return TRUE;
	}

	/**
	 * Connect to the database
	 * @return boolean
	 * @throws Nina_Exception_Db If connection can't be made
	 */
	public function connect() {
		try {
			if ($this->reg->exists('username,password'))
				$this->pdo = new PDO($this->reg->get('dsn'), $this->reg->get('username'), $this->reg->get('password'));
			elseif ($this->reg->exists('username'))
				$this->pdo = new PDO($this->reg->get('dsn'), $this->reg->get('user'));
			else
				$this->pdo = new PDO($this->reg->get('dsn'));

			return TRUE;
		}
		catch (PDOException $e) {
			throw new Nina_Exception_Db($e);
			return FALSE;
		}
	}

	/**
	 * Show that this is a DB adapter
	 * @return true
	 */
	public function database() {
		return true;
	}

	/**
	 * Prepare SQL
	 * @param string $sql SQL
	 * @return Nina_Db_Driver_Statement_Pdo
	 */
	public function prepare($sql) {
		$stmt = new Nina_Db_Driver_Statement_Pdo();
		$stmt->setDb($this);
		$stmt->setPdo($this->pdo);
		$stmt->setSql($sql);
		return $stmt;
	}

	/**
	 * Get the database fields
	 * @param string $table Table name
	 * @return array
	 */
	public function getFields($table) {
		$rs = $this->query('SHOW COLUMNS FROM ' . $table);
		if ($rs->count()) {
			$fields = array();

			while ($row = $rs->row()) {
				$fields[] = $row['Field'];
			}

			return $fields;
		}
		else
			return false;
	}

	/**
	 * Get the database tables
	 * @return array
	 */
	public function getTables() {
		$rs = $this->query('SHOW TABLES');
		if ($rs->count()) {
			$tables = array();

			while ($row = $rs->row()) {
				$tables[] = reset($row);
			}

			return $tables;
		}
		else
			return false;
	}

	/**
	 * Run a query against the database.
	 * @param string $query Query
	 * @return Nina_Db_Driver_Result_Pdo
	 */
	public function query($query) {
		$stmt = $this->pdo->prepare($query, array(PDO::ATTR_CURSOR => PDO::CURSOR_SCROLL));
		if (!$stmt) //SQLite for one, does not support scrolling cursors
		{
			$stmt = $this->pdo->prepare($query);
		}
		if (!$stmt) { return false; }
		
		$stmt->execute();
		$rs = new Nina_Db_Driver_Result_Pdo($this->pdo);
		$rs->setPdoResult($stmt);
		return $rs;
	}
	/**
	 * Start a transaction in PDO.
	 */
	public function begin() {
		$this->pdo->beginTransaction();
	}
	/**
	 * Commit a transaction in PDO.
	 */
	public function commit() {
		$this->pdo->commit();
	}
	/**
	 * Rollback a transaction in PDO.
	 */
	public function rollback() {
		$this->pdo->rollBack();
	}
	/**
	 * Escape data for insertion into the database.
	 * @param string $Input
	 * @return string
	 */
	public function escape($data) {
		return $this->pdo->quote($data);
	}
	/**
	 * Get the ID of the last inserted row.
	 * @return int
	 */
	public function insertId() {
		return $this->pdo->lastInsertId();
	}
	public function lastError() {
		$err = $this->pdo->errorInfo();
		if ($err) {
			return implode(" : ", $err);
		}
	}

	public function ping() {

	}

	public function close() {

	}
}
